<?php
/**
 * @author 595949289@qq.com
 * @date 2023/9/6 11:32
 */

namespace CuiFox\oauth2server;

use filsh\yii2\oauth2server\Response;
use Yii;

/**
 * For example,
 *
 * ```php
 * 'oauth2' => [
 *     'class' => 'CuiFox\yii2\oauth2server\Module',
 *     'tokenParamName' => 'access_token', // 访问令牌参数名称（可选）
 *     'tokenAccessLifetime' => 3600, // 访问令牌有效期（可选）
 *     'storageMap' => [
 *         'user_credentials' => 'app\models\User', // 用于验证用户凭据的存储器（根据你的应用需求替换）
 *         'user_claims' => 'app\models\User', // 用于验证用户凭据的存储器（根据你的应用需求替换）
 *     ],
 *     'grantTypes' => [
 *         'user_credentials' => [
 *             'class' => 'OAuth2\GrantType\UserCredentials',
 *         ],
 *         'user_credentials' => [
 *             'class' => 'OAuth2\GrantType\UserCredentials',
 *         ],
 *         'client_credentials' => [
 *             'class' => 'OAuth2\GrantType\ClientCredentials',
 *         ],
 *         'refresh_token' => [
 *             'class' => 'OAuth2\GrantType\RefreshToken',
 *             'config' => [
 *                 'always_issue_new_refresh_token' => true,
 *             ],
 *         ],
 *         'jwt_bearer' => [
 *             'class' => 'OAuth2\GrantType\JwtBearer',
 *             'audience' => 'http://yii-abc.com',
 *         ],
 *     ]
 * ]
 * ```
 */
class Module extends \filsh\yii2\oauth2server\Module
{
    /**
     * @return \filsh\yii2\oauth2server\Server|null|object
     * @throws \ReflectionException
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getServer()
    {
        if (!$this->has('server')) {
            $storages = [];

            if ($this->useJwtToken) {
                if (!array_key_exists('access_token', $this->storageMap) || !array_key_exists('public_key',
                        $this->storageMap)) {
                    throw new \yii\base\InvalidConfigException('access_token and public_key must be set or set useJwtToken to false');
                }
                //define dependencies when JWT is used instead of normal token
                Yii::$container->clear('public_key'); //remove old definition
                Yii::$container->set('public_key', $this->storageMap['public_key']);
                Yii::$container->set('OAuth2\Storage\PublicKeyInterface', $this->storageMap['public_key']);

                Yii::$container->clear('access_token'); //remove old definition
                Yii::$container->set('access_token', $this->storageMap['access_token']);
            }

            foreach (array_keys($this->storageMap) as $name) {
                $storages[$name] = Yii::$container->get($name);
            }

            $grantTypes = [];
            foreach ($this->grantTypes as $name => $options) {
                if (!isset($storages[$name]) || empty($options['class'])) {
                    throw new \yii\base\InvalidConfigException('Invalid grant types configuration.');
                }

                $class = $options['class'];
                unset($options['class']);

                $reflection = new \ReflectionClass($class);
                $config = array_merge([0 => $storages[$name]], $options);

                $instance = $reflection->newInstanceArgs($config);
                $grantTypes[$name] = $instance;
            }

            $server = Yii::$container->get(Server::className(), [
                $this,
                $storages,
                array_merge(array_filter([
                    'use_jwt_access_tokens' => $this->useJwtToken,
                    'token_param_name' => $this->tokenParamName,
                    'access_lifetime' => $this->tokenAccessLifetime,
                    /** add more ... */
                ]), $this->options),
                $grantTypes,
            ]);

            $this->set('server', $server);
        }
        return $this->get('server');
    }

    /**
     * @return Response|null|object
     * @throws \yii\base\InvalidConfigException
     */
    public function getResponse()
    {
        if(!$this->has('response')) {
            $this->set('response', new Response());
        }

        $response = $this->get('response');

        return $response instanceof \yii\web\Response ? new Response() : $response;
    }
}